#ifndef MOLCLUSTER_H
#define MOLCLUSTER_H


#include <vector.h>
#include <fstream.h>
#include "atom.h"

using namespace std;

extern double mt();

struct bond{ 
	char label1;
	char label2; 
	double maxDistance;
};


/*! \file */
//! This class provides an abstraction of a cluster of molecules.

/*! This class describes a cluster of molecules with different operations to support Evolutionary Algorithm such as evaluate, mutate, and crossover. Note that validity is always assured at anytime. */


class molCluster {

	//! This friend function output the info of the molCluster <aMolCluster> into an output stream <out>.
        /*!
                 \param out an output stream.
                 \param aMolCluster a molCluster which info is to be printed out.
        */
	friend ostream& operator<<( ostream& out,  molCluster& aMolCluster ); 

public:
	int nMolecules;
	int nAtoms;
	int nUniqueBonds;
	vector<double>(*evaluateFunc)( vector<atom>& atomsArg, int optimize );
	

	vector < atom > atoms;
	vector < double > properties;
	double signature[ SIGLENGTH ];
	vector < vector< int > > molConnectList;	
	vector < vector< char > > molLabelList;
	vector < bond > molBondList;	

	/*! \brief constructor */
	molCluster( );
	
	//! This copy constructor generates the molCluster with another molCluster's values.
        /*!
	        \param rhs another molCluster to be copied.
        */
	molCluster( const molCluster& rhs );

	//! This constructor generates the molCluster with the input values.
        /*!
                 \param settingFileName name of file containing the molCluster setting, see molSetting.dat for example.
                 \param anEvaluator pointer to evaluator function.
        */
	molCluster( string& settingFileName, vector<double>(*anEvaluator)(vector<atom>& a, int optimize ));
	
	/*! \brief destructor */
	~molCluster( );	

	//! This function returns the number of atoms in the molCluster.
	int size( );
	
	//! This function obtains the centroid of the molCluster.
        /*!
                 \param centroid an atom to hold the centroid values.
        */
	void getCentroid( atom &centroid );
	
	//! This function positions the molCluster relative to the centroid.
	void setToCentroid( );

	//! This function rotates a point p by angle theta around an arbitrary axis r.
	/*!
		\param p atom to be rotated.
		\param q resultant atom.
		\param theta angle of rotation.
		\param r an arbitrary axis(xyz).
	*/
	void ArbitraryRotate( atom p, atom& q, double theta, atom r );

 	//! This function normalises the atom <p>'s coordinates to unit vector.
        /*!
                \param p an atom which coordinates are to be be normalised.
        */
	void normalise( atom& p );

	//! This function rotates the molCluster for angle of <theta>.
        /*!
               \param theta angle of rotation.
        */
	void rotateAboutCentroid( double theta );
	
	//! This function prints the content of the molCluster to file output stream <out>.
        /*!
                \param out file output stream.
        */
	void printToFileStream( ofstream& out ); 
	
	//! Assignment(=) operator.
        /*!
                \param rhs another molCluster to be assigned to the invoking molCluster.
        */
	molCluster& operator=( const molCluster& rhs );

	//! Index([]) operator. This operator returns atom <index>.
        /*!
               \param index index of atom to be retrieved.
        */
	atom& operator[]( int index );
	
	
	//! This function gets statistics from an atom a.
        /*!
                \param a an atom from which statistics is calculated.
		\param mean mean/average.
		\param sd standard deviation.
		\param assymetry assymetry value.
        */
	bool getStatistics( atom& a , double& mean, double& sd, double& assymetry );
	
	//! This function calculate the molCluster's signature.
	bool calcSignature( );

	//! This function get the signature of the molCluster.
        /*!
                 \param sig signature obtained.
        */
	bool getSignature( vector< double >& sig );

	//! This function resets signature values.
	void resetSignature( );

	//! This function returns the similarity between the molCluster and another molCluster <rhs>.
        /*!
                 \param rhs another molCluster to which similarity is calculated.
        */
	double getSimilarityTo( molCluster& rhs );

	//! This function returns the euclidean distance of the molCluster's signature to <rhs>'s signature.
        /*!
                 \param rhs another molCluster to which distance is calculated.
        */
	double getSigEucDistanceTo( molCluster& rhs );

	//! This function gets the furthest atom from atom <c> and stores it in atom <fatom>.
        /*!
                \param c atom from which furthest atom is sought from.
                \param fatom furthest atom from atom <c> obtained.
        */
	int getFurthestAtomFrom( atom& c, atom& fatom );

	//! This function get the closest atom to atom <c>.
        /*!
                \param c the reference atom.
		\param catom the nearest atom atom <c>.
        */
	int getClosestAtomTo( atom& c, atom& catom );
	
	//! This function moves a molecule of index <pickedMol> to a random spot at a maximum distance of <maxAxisDistance>.
        /*!
                \param pickedMol index of picked molecule to move.
		\param maxAxisDistance maximum axis distance to move.
        */
	void molTranslation( int pickedMol, double maxAxisDistance );
	
	//! This function rotates a molecule of index <pickedMol>.
        /*!
                \param pickedMol index of picked molecule to rotate.
        */
	void molRotation( int pickedMol );

	//! This function moves a molecule of index <pickedMol> to another spot on the surface of the cluster.
	/*!
		\param pickedMol index of picked molecule to relocate.
	*/
	void relocateMol( int pickedMol );

	//! This function evaluate fitness of the molCluster.
        /*!
                 \param optimize 0 for evaluate only, 1 for optimize.
        */
	void evaluate( int optimize );

	//! This function crossovers the molCluster(assuming the invoking molCluster is <dad>) with another molCluster <mum>.
        /*!
                 \param mum another molCluster which crossover is performed with.
                 \param child the resultant molCluster from crossover.
        */
	void crossover( molCluster mum, molCluster &child );

	//! This function mutates the molCluster.
        /*!
                 \param strength number of mutation iteration.
        */
	void mutate( int strength );	

	//! This function checks the validity of the molCluster.
	bool isValid();

private:
	//! This auxiliary function checks the disassociation of a molCluster.
        /*!
                \param d 2D(<n>-by-<n>) matrix containing the connectivity info of the atoms.
		\param n the size of matrix <d>.
        */
	bool bConnectivity( vector<vector<int> > d,int n );

	//! This auxiliary function checks whether 2 atoms, represented by label <a> and <b> separated by <distance> are connected.
        /*!
                \param a label of first atom.
		\param b label of second atom.
		\param distance distance of separation between the two atoms.
        */
	bool checkBondConnect( char a, char b, double distance );

};

#endif

